package org.jeecg.modules.flowable.service.action;

import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.jeecg.modules.flowable.common.util.IdUtil;
import org.jeecg.modules.flowable.common.util.LnchRedisUtil;
import org.jeecg.modules.flowable.common.util.validation.AssertValid;
import org.jeecg.modules.flowable.common.util.validation.MustParamValidation;
import org.jeecg.modules.flowable.entity.TActMatterEntity;
import org.jeecg.modules.flowable.entity.TActMatterInstEntity;
import org.jeecg.modules.flowable.exception.ModuleFlowableException;
import org.jeecg.modules.flowable.mapper.TActMatterInstMapper;
import org.jeecg.modules.flowable.mapper.TActMatterMapper;
import org.jeecg.modules.flowable.service.action.event.ActionEvent;
import org.jeecg.modules.flowable.service.oform.OformDeal;
import org.jeecg.modules.flowable.util.SpringContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;

/**
 * Title:  <br>
 * Desc:  <br>
 * Date: 6/26/22 10:12 AM <br>
 * Company: 联通（辽宁）产业互联网有限公司 <br>
 *
 * @author zxd
 * @version 1.0.0
 */
@Service
@Slf4j
public class ActionFactory {

    @Autowired
    private LnchRedisUtil redisUtil;

    @Autowired
    private TaskService taskService;

    @Autowired
    private TActMatterInstMapper tActMatterInstMapper;

    @Autowired
    private TActMatterMapper tActMatterMapper;

    /**
     * 动作处理
     *
     * @param argActionName
     * @param argActionBody
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean deal(String argActionName, ActionBody argActionBody, ActionEvent argEvent, String argActionFunname) {
        try {
            // 得到处理函数
            ActionExec exec = null;
            try {
                exec = (ActionExec) SpringContextUtil.getBean(argActionName);
            } catch (Exception e) {
                throw new ModuleFlowableException("名称为:" + argActionName + "的动作不存在");
            }
            MustParamValidation.mustValidation(argActionBody.getMatterInstId(), argActionBody.getTaskId());
            String lockPwd = IdUtil.getStringId();
            // 加锁及验证
            lock(argActionBody.getTaskId(), lockPwd);
            // 开始事件
            if (argEvent != null) {
                log.info("执行开始事件");
                try {
                    argEvent.beginAction(argActionBody);
                } catch (Exception e) {
                    // 解锁
                    unlock(argActionBody.getTaskId(), lockPwd);
                    throw new ModuleFlowableException(e);
                }
            }
            try {
                // 查找流程定义KEYT_ACT_MATTER_INST
                TActMatterInstEntity tActMatterInstEntity = tActMatterInstMapper.selectById(argActionBody.getMatterInstId());
                TActMatterEntity tActMatterEntity = tActMatterMapper.selectById(tActMatterInstEntity.getActMatterId());
                // 业务表单数据处理BEAN埋点
                String oFormBeanName = "OformDeal_" + tActMatterEntity.getFlowKey() + "_" + argActionName;
                Object bean = null;
                try {
                    bean = SpringContextUtil.getBean(oFormBeanName);
                } catch (Exception e) {
                    // 走到这里说明没有定义对应动作的处理类，不需要处理这个异常，流程继续往下走
                }
                if (bean != null && bean instanceof OformDeal) {
                    ((OformDeal) bean).deal(argActionBody, argActionFunname);
                }
                // 通用处理业务BEAN，就是所有的动作都会执行的业务BEAN埋点
                oFormBeanName = "OformDeal_" + tActMatterEntity.getFlowKey();
                bean = null;
                try {
                    bean = SpringContextUtil.getBean(oFormBeanName);
                } catch (Exception e) {
                    // 走到这里说明没有定义对应动作的处理类，不需要处理这个异常，流程继续往下走
                }
                if (bean != null && bean instanceof OformDeal) {
                    ((OformDeal) bean).deal(argActionBody, argActionFunname);
                }
                // 动作代码
                if (AssertValid.isEmpty(argActionBody.getAttDesc()))
                    argActionBody.setAttDesc("");
                if (AssertValid.isEmpty(argActionBody.getFormData()))
                    argActionBody.setFormData(new HashMap<>());
                if (AssertValid.isEmpty(argActionBody.getAttType()))
                    argActionBody.setAttType(3);
                if (AssertValid.isEmpty(argActionBody.getAttDesc()))
                    argActionBody.setAttDesc("");
                exec.deal(argActionBody, argActionFunname, argActionName);
            } catch (Exception e) {
                // 解锁
                unlock(argActionBody.getTaskId(), lockPwd);
                throw new ModuleFlowableException(e);
            }
            // 宛成事件
            if (argEvent != null) {
                log.info("执行完成事件");
                try {
                    argEvent.endAction(argActionBody);
                } catch (Exception e) {
                    // 解锁
                    unlock(argActionBody.getTaskId(), lockPwd);
                    throw new ModuleFlowableException(e);
                }
            }
            // 解锁
            unlock(argActionBody.getTaskId(), lockPwd);
            return true;
        } catch (Exception e) {
            throw new ModuleFlowableException(e.getMessage());
        }
    }

    // 任务处理验证及加锁
    private void lock(String argTeskId, String argPassword) {
        String key = "flowable_tasklock_" + argTeskId;
        Boolean lock = redisUtil.lock(key, argPassword, 300);
        if (!lock) {
            throw new ModuleFlowableException("任务正在处理中");
        }
        // 验证任务是否已被处理
        List<Task> list = taskService.createTaskQuery().taskId(argTeskId).list();
        if (AssertValid.isEmpty(list)) {
            unlock(argTeskId, argPassword);
            throw new ModuleFlowableException("任务不存在或已经被处理");
        }
    }

    // 解锁
    private void unlock(String argTeskId, String argPassword) {
        String key = "flowable_tasklock_" + argTeskId;
        redisUtil.unlock(key, argPassword);
    }
}